home *** CD-ROM | disk | FTP | other *** search
/ 3D GFX / 3D GFX.iso / amiutils / i_l / irit5 / cagd_lib / cagdswep.c < prev    next >
C/C++ Source or Header  |  1995-12-30  |  21KB  |  545 lines

  1. /******************************************************************************
  2. * CagdSwep.c - Sweep srf operator out of given cross section and an axis.     *
  3. *******************************************************************************
  4. * Written by Gershon Elber, May. 91.                          *
  5. ******************************************************************************/
  6.  
  7. #include "cagd_loc.h"
  8.  
  9. #define MAX_AXIS_REFINE_LEVEL 50
  10.  
  11. static void TransformCrossSection(CagdRType **SPoints,
  12.                   int Index,
  13.                   CagdCrvStruct *CrossSection,
  14.                   CagdRType *Position,
  15.                   CagdRType Scale, 
  16.                   CagdRType Weight,
  17.                   CagdVecStruct *Tangent,
  18.                   CagdVecStruct *Normal);
  19. static void GenTransformMatrix(CagdMType Mat,
  20.                    CagdRType *Trans,
  21.                    CagdVecStruct *Normal,
  22.                    CagdVecStruct *Tangent,
  23.                    CagdRType Scale,
  24.                    CagdRType Weight);
  25.  
  26. /*****************************************************************************
  27. * DESCRIPTION:                                                               M
  28. * Constructs a sweep surface using the following curves:                     M
  29. * 1. CrossSection - defines the basic cross section of the sweep. Must be    M
  30. *    in the XY plane.                                 M
  31. * 2. Axis - a 3D curve the CrossSection will be swept along such that the    M
  32. *    Axis normal aligns with the Y axis of the cross section. If Axis is     M
  33. *    linear (i.e. no normal), the normal is picked randomly or to fit the    M
  34. *    non linear part of the Axis (if any).                     M
  35. * 3. Scale - a scaling curve for the sweep, If NULL a scale of Scale is      M
  36. *    used.                                     M
  37. * 4. Frame - a curve or a vector that specifies the orientation of the sweep M
  38. *    by specifying the axes curve's binormal. If Frame is a vector, it is a  M
  39. *    constant binormal. If Frame is a curve (FrameIsCrv = TRUE), it is       M
  40. *    assumed to be a vector field binormal. If NULL, it is computed from     M
  41. *    the Axis curve's pseudo Frenet frame, that minimizes rotation.         M
  42. *                                                                            M
  43. *   This operation is only an approximation. See CagdSweepAxisRefine for a   M
  44. * tool to refine the Axis curve and improve accuracy.                        M
  45. *                                                                            *
  46. * PARAMETERS:                                                                M
  47. *   CrossSection:  Of the constructed sweep surface.                         M
  48. *   Axis:          Of the constructed sweep surface.                         M
  49. *   ScalingCrv:    Optional scale or profiel curve.                          M
  50. *   Scale:         if no Scaling Crv, Scale is used to apply a fixed scale   M
  51. *                  on the CrossSection curve.                                M
  52. *   Frame:         An optional vector or a curve to specified the binormal   M
  53. *                  orientation. Otherwise Frame must be NULL.             M
  54. *   FrameIsCrv:    If TRUE Frame is a curve, if FALSE a vector (if Frame is  M
  55. *                  not NULL).                                 M
  56. *                                                                            *
  57. * RETURN VALUE:                                                              M
  58. *   CagdSrfStruct *:  Constructed sweep surface.                             M
  59. *                                                                            *
  60. * KEYWORDS:                                                                  M
  61. *   CagdSweepSrf, sweep, surface constructors                                M
  62. *****************************************************************************/
  63. CagdSrfStruct *CagdSweepSrf(CagdCrvStruct *CrossSection,
  64.                 CagdCrvStruct *Axis,
  65.                 CagdCrvStruct *ScalingCrv,
  66.                 CagdRType Scale,
  67.                 VoidPtr Frame,
  68.                 CagdBType FrameIsCrv)
  69. {
  70.     CagdSrfStruct
  71.     *Srf = NULL;
  72.     CagdVecStruct Normal, *Vec, TVec;
  73.     CagdPointType
  74.     SrfPType = CAGD_PT_E3_TYPE;
  75.     CagdGeomType
  76.     SrfGType = CAGD_SBSPLINE_TYPE;
  77.     int i, j, k,
  78.     ULength = CrossSection -> Length,
  79.     VLength = Axis -> Length,
  80.     UOrder = CrossSection -> Order,
  81.     VOrder = Axis -> Order;
  82.     CagdRType **Points,
  83.     *AxisKV = NULL,
  84.     *AxisNodes = NULL,
  85.     *AxisNodePtr = NULL,
  86.     *AxisWeights = NULL,
  87.     *FrameVec = FrameIsCrv ? NULL : (CagdRType *) Frame;
  88.     CagdCrvStruct
  89.     *FrameCrv = FrameIsCrv ? (CagdCrvStruct *) Frame : NULL;
  90.  
  91.     switch (Axis -> GType) {
  92.     case CAGD_CBEZIER_TYPE:
  93.         SrfGType = CAGD_SBEZIER_TYPE;
  94.         AxisKV = BspKnotUniformOpen(VLength, VOrder, NULL);
  95.         AxisNodes = AxisNodePtr = BspKnotNodes(AxisKV,
  96.                            VLength + VOrder,
  97.                            VOrder);
  98.         break;
  99.     case CAGD_CBSPLINE_TYPE:
  100.         SrfGType = CAGD_SBSPLINE_TYPE;
  101.         AxisKV = Axis -> KnotVector;
  102.         AxisNodePtr = AxisNodes = BspKnotNodes(Axis -> KnotVector,
  103.                            VLength + VOrder,
  104.                            VOrder);
  105.         if (Axis -> Periodic) {
  106.         /* Nodes will give us very skewed samples. Take middle of */
  107.         /* every interior span as samples for axis positioning.   */
  108.         for (i = 0; i < VLength; i++)
  109.             AxisNodes[i] = (AxisKV[i + VOrder - 1] +
  110.                     AxisKV[i + VOrder]) / 2.0;
  111.         }
  112.         break;
  113.     case CAGD_CPOWER_TYPE:
  114.         CAGD_FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
  115.         break;
  116.     default:
  117.         CAGD_FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
  118.         break;
  119.     }
  120.     if (CAGD_IS_RATIONAL_CRV(Axis))
  121.     AxisWeights = Axis -> Points[W];
  122.  
  123.     switch (CrossSection -> GType) {
  124.     case CAGD_CBEZIER_TYPE:
  125.         break;
  126.     case CAGD_CBSPLINE_TYPE:
  127.         SrfGType = CAGD_SBSPLINE_TYPE;
  128.         break;
  129.     case CAGD_CPOWER_TYPE:
  130.         CAGD_FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
  131.         break;
  132.     default:
  133.         CAGD_FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
  134.         break;
  135.     }
  136.  
  137.     if (ScalingCrv) {
  138.     int ScaleKVLen,
  139.         AxisKVLen = Axis -> Order + Axis -> Length;
  140.  
  141.     switch (ScalingCrv -> GType) {
  142.         case CAGD_CBEZIER_TYPE:
  143.         ScalingCrv = CnvrtBezier2BsplineCrv(ScalingCrv);
  144.             break;
  145.         case CAGD_CBSPLINE_TYPE:
  146.             ScalingCrv = CagdCrvCopy(ScalingCrv);
  147.         break;
  148.         case CAGD_CPOWER_TYPE:
  149.         CAGD_FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
  150.         break;
  151.         default:
  152.         CAGD_FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
  153.         break;
  154.     }
  155.  
  156.     ScaleKVLen = ScalingCrv -> Order + ScalingCrv -> Length;
  157.  
  158.         /* Affine trans. ScalingCrv KV to match Axis. */
  159.     BspKnotAffineTrans(ScalingCrv -> KnotVector, ScaleKVLen,
  160.                AxisKV[0] - ScalingCrv -> KnotVector[0],
  161.                (AxisKV[AxisKVLen - 1] - AxisKV[0]) /
  162.                (ScalingCrv -> KnotVector[ScaleKVLen - 1] -
  163.                 ScalingCrv -> KnotVector[0]));
  164.     }
  165.  
  166.     if (FrameCrv) {
  167.     int FrameKVLen,
  168.         AxisKVLen = Axis -> Order + Axis -> Length;
  169.  
  170.     switch (FrameCrv -> GType) {
  171.         case CAGD_CBEZIER_TYPE:
  172.         FrameCrv = CnvrtBezier2BsplineCrv(FrameCrv);
  173.             break;
  174.         case CAGD_CBSPLINE_TYPE:
  175.             FrameCrv = CagdCrvCopy(FrameCrv);
  176.         break;
  177.         case CAGD_CPOWER_TYPE:
  178.         CAGD_FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
  179.         break;
  180.         default:
  181.         CAGD_FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
  182.         break;
  183.     }
  184.  
  185.     FrameKVLen = FrameCrv -> Order + FrameCrv -> Length;
  186.  
  187.         /* Affine trans. FrameCrv KV to match Axis. */
  188.     BspKnotAffineTrans(FrameCrv -> KnotVector, FrameKVLen,
  189.                AxisKV[0] - FrameCrv -> KnotVector[0],
  190.                (AxisKV[AxisKVLen - 1] - AxisKV[0]) /
  191.                (FrameCrv -> KnotVector[FrameKVLen - 1] -
  192.                 FrameCrv -> KnotVector[0]));
  193.     }
  194.  
  195.     if (CAGD_IS_RATIONAL_CRV(Axis) || CAGD_IS_RATIONAL_CRV(CrossSection))
  196.     SrfPType = CAGD_PT_P3_TYPE;
  197.  
  198.     switch (SrfGType) {
  199.     case CAGD_SBEZIER_TYPE:
  200.         Srf = BzrSrfNew(ULength, VLength, SrfPType);
  201.         break;
  202.     case CAGD_SBSPLINE_TYPE:
  203.         Srf = BspPeriodicSrfNew(ULength, VLength, UOrder, VOrder,
  204.                     CrossSection -> Periodic, Axis -> Periodic,
  205.                     SrfPType);
  206.         if (CrossSection -> GType == CAGD_CBSPLINE_TYPE)
  207.         CAGD_GEN_COPY(Srf -> UKnotVector, CrossSection -> KnotVector,
  208.                   sizeof(CagdRType) *
  209.                  (CAGD_CRV_PT_LST_LEN(CrossSection) + UOrder));
  210.         else
  211.         BspKnotUniformOpen(ULength, UOrder, Srf -> UKnotVector);
  212.         if (Axis -> GType == CAGD_CBSPLINE_TYPE)
  213.         CAGD_GEN_COPY(Srf -> VKnotVector, Axis -> KnotVector,
  214.                   sizeof(CagdRType) *
  215.                       (CAGD_CRV_PT_LST_LEN(Axis) + VOrder));
  216.         else
  217.         BspKnotUniformOpen(VLength, VOrder, Srf -> VKnotVector);
  218.         break;
  219.     default:
  220.         CAGD_FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
  221.         break;
  222.     }
  223.     Points = Srf -> Points;
  224.  
  225.     /* Compute the first normal to be used to orient first cross section. */
  226.     if (FrameVec == NULL && FrameCrv == NULL) {
  227.     /* Compute the normal to axis curve and use it to align the +Y axis  */
  228.     /* of cross section with that vector. If Axis curve has no normal    */
  229.     /* (i.e. it is a linear segment), an arbitrary normal is picked.     */
  230.     Vec = CagdCrvNormal(Axis, *AxisNodePtr);
  231.     if (Vec != NULL) {
  232.         CAGD_COPY_VECTOR(Normal, *Vec);
  233.     }
  234.     else {
  235.         Vec = CagdCrvTangent(Axis, *AxisNodePtr);
  236.         Normal.Vec[0] = Normal.Vec[1] = Normal.Vec[2] = 0.0;
  237.         if (ABS(Vec -> Vec[0]) <= ABS(Vec -> Vec[1]) &&
  238.         ABS(Vec -> Vec[0]) <= ABS(Vec -> Vec[2]))
  239.         Normal.Vec[0] = 1.0;
  240.         else if (ABS(Vec -> Vec[1]) <= ABS(Vec -> Vec[0]) &&
  241.              ABS(Vec -> Vec[1]) <= ABS(Vec -> Vec[2]))
  242.         Normal.Vec[1] = 1.0;
  243.         else
  244.         Normal.Vec[2] = 1.0;
  245.  
  246.         CROSS_PROD(TVec.Vec, Vec -> Vec, Normal.Vec);
  247.         CAGD_NORMALIZE_VECTOR(TVec);
  248.         CROSS_PROD(Normal.Vec, TVec.Vec, Vec -> Vec);
  249.     }
  250.     }
  251.  
  252.     /* For each ctl points of the axis, transform the cross section          */
  253.     /* according to ctl point position, tangent to axis at the point and in  */
  254.     /* such a way to minimize Normal change.                     */
  255.     for (i = 0; i < VLength; i++, AxisNodePtr++) {
  256.     CagdRType PosE3[3], ScaleE2[2],
  257.         *Scaling = ScalingCrv ? CagdCrvEval(ScalingCrv, *AxisNodePtr)
  258.                   : NULL;
  259.     CagdVecStruct
  260.         *Tangent = CagdCrvTangent(Axis, *AxisNodePtr);
  261.  
  262.     if (Scaling)
  263.         CagdCoerceToE2(ScaleE2, &Scaling, -1, ScalingCrv -> PType);
  264.     else
  265.         ScaleE2[1] = Scale;
  266.  
  267.     /* If Normal is fully specified, get it now. */
  268.     if (FrameVec != NULL) {
  269.         CROSS_PROD(Normal.Vec, FrameVec, Tangent -> Vec);
  270.         CAGD_NORMALIZE_VECTOR(Normal);
  271.     }
  272.     else if (FrameCrv != NULL) {
  273.         CagdRType Binormal[3],
  274.         *FrameCrvVal = CagdCrvEval(FrameCrv, *AxisNodePtr);
  275.  
  276.         CagdCoerceToE3(Binormal, &FrameCrvVal, -1, FrameCrv -> PType);
  277.  
  278.         CROSS_PROD(Normal.Vec, Binormal, Tangent -> Vec);
  279.         CAGD_NORMALIZE_VECTOR(Normal);
  280.     }
  281.  
  282.     if (Axis -> Periodic) {
  283.         CagdRType
  284.         *R = CagdCrvEval(Axis, *AxisNodePtr);
  285.  
  286.         CagdCoerceToE3(PosE3, &R, -1, Axis -> PType);
  287.     }
  288.     else
  289.         CagdCoerceToE3(PosE3, Axis -> Points, i, Axis -> PType);
  290.     TransformCrossSection(Points, i * ULength, CrossSection,
  291.                   PosE3, ScaleE2[1],
  292.                   AxisWeights ? AxisWeights[i] : 1.0,
  293.                   Tangent, &Normal);
  294.     }
  295.  
  296.     /* Do fixups if axis is a rational curve (note surface is P3). */
  297.     if (AxisWeights) {
  298.     if (CAGD_IS_RATIONAL_CRV(CrossSection)) {
  299.         /* Need only scale by the Axis curve weights: */
  300.         for (j = 0, k = 0; j < VLength; j++)
  301.         for (i = 0; i < ULength; i++, k++) {
  302.             Points[X][k] *= AxisWeights[j];
  303.             Points[Y][k] *= AxisWeights[j];
  304.             Points[Z][k] *= AxisWeights[j];
  305.             Points[W][k] *= AxisWeights[j];
  306.         }
  307.     }
  308.     else {
  309.         /* Weights do not exists at the moment - need to copy them. */
  310.         for (j = 0, k = 0; j < VLength; j++)
  311.         for (i = 0; i < ULength; i++, k++) {
  312.             Points[X][k] *= AxisWeights[j];
  313.             Points[Y][k] *= AxisWeights[j];
  314.             Points[Z][k] *= AxisWeights[j];
  315.             Points[W][k] = AxisWeights[j];
  316.         }
  317.     }
  318.     }
  319.  
  320.     if (Axis -> GType == CAGD_CBEZIER_TYPE)
  321.         IritFree((VoidPtr) AxisKV);
  322.     if (ScalingCrv)
  323.     CagdCrvFree(ScalingCrv);
  324.     IritFree((VoidPtr) AxisNodes);
  325.  
  326.     return Srf;
  327. }
  328.  
  329. /*****************************************************************************
  330. * DESCRIPTION:                                                               *
  331. * Transforms the CrossSection Points, to Position such that Tangent is       *
  332. * perpendicular to the cross section (which is assumed to be on the XY       *
  333. * plane). The +Y axis of the cross section is aligned with Normal direction  *
  334. * to minimize twist along the sweep and been updated to new normal.         *
  335. *   Transformed cross section is place into srf Points, SPoints starting     *
  336. * from index SIndex.                                 *
  337. *   All agrument vectors are assumed to be normalized to a unit length.         *
  338. *                                                                            *
  339. * PARAMETERS:                                                                *
  340. *   SPoints:        Final destination of the transformed points.             *
  341. *   SIndex:         Index in SPOints where to start and place new points.    *
  342. *   CrossSection:   To transform and place in SPoints.                       *
  343. *   Position:       Translation factor.                                      *
  344. *   Scale:          Scale factor.                                            *
  345. *   Weight:         Weight factor if rational.                               *
  346. *   Tangent:        Tangent direction to prescribe orientaion.               *
  347. *   Normal:         Normal direction to prescribe orientaion.                *
  348. *                                                                            *
  349. * RETURN VALUE:                                                              *
  350. *   void                                                                     *
  351. *****************************************************************************/
  352. static void TransformCrossSection(CagdRType **SPoints,
  353.                   int SIndex,
  354.                   CagdCrvStruct *CrossSection,
  355.                   CagdRType *Position,
  356.                   CagdRType Scale,
  357.                   CagdRType Weight,
  358.                   CagdVecStruct *Tangent,
  359.                   CagdVecStruct *Normal)
  360. {
  361.     CagdBType
  362.     IsNotRational = !CAGD_IS_RATIONAL_PT(CrossSection -> PType);
  363.     CagdVecStruct TVec;
  364.     CagdMType Mat;
  365.     CagdCrvStruct
  366.     *CSCopy = CagdCrvCopy(CrossSection);
  367.     int i, j, MaxCoord,
  368.     Len = CSCopy -> Length;
  369.     CagdRType R,
  370.     **CSPoints = CSCopy -> Points;
  371.  
  372.     /* Fix Normal to be perpendicular to the Tangent vector for a minimal */
  373.     /* rotation. This ensures minimal twist in the resulting surface.      */
  374.     R = DOT_PROD(Normal -> Vec, Tangent -> Vec);
  375.     TVec = *Tangent;
  376.     CAGD_MULT_VECTOR(TVec, R);
  377.     CAGD_SUB_VECTOR(*Normal, TVec);
  378.     CAGD_NORMALIZE_VECTOR(*Normal);
  379.  
  380.     GenTransformMatrix(Mat, Position, Normal, Tangent, Scale, Weight);
  381.     CagdCrvMatTransform(CSCopy, Mat);
  382.  
  383.     /* Max coord. may be modified by CagdCrvMatTransform to be 3D if was 2D! */
  384.     MaxCoord = CAGD_NUM_OF_PT_COORD(CSCopy -> PType);
  385.  
  386.     for (i = 0; i < Len; i++)
  387.     for (j = IsNotRational; j <= MaxCoord; j++)
  388.         SPoints[j][SIndex + i] = CSPoints[j][i];
  389.  
  390.     CagdCrvFree(CSCopy);
  391. }
  392.  
  393. /*****************************************************************************
  394. * DESCRIPTION:                                                               *
  395. *   Routine to prepar a transformation martix to do the following (in this   *
  396. * order): scale by Scale/Weight, rotate such that X axis is in Normal dir    *
  397. * and Y is colinear with the BiNormal and then translate by Trans.         *
  398. *    Algorithm: given the Trans vector, it forms the 4th line of Mat. Dir is *
  399. * used to form the second line (the first 3 lines set the rotation), and     *
  400. * finally Scale is used to scale first 3 lines/columns to the needed scale:  *
  401. *                |  Nx  Ny  Nz  0 |   A transformation which takes the coord *
  402. *                |  Bx  By  Bz  0 |  system into T, N & B as required and    *
  403. * [X  Y  Z  1] * |  Tx  Ty  Tz  0 |  then translate it to C. T, N, B are     *
  404. *                |  Cx  Cy  Cz  1 |  scaled by Scale.                 *
  405. * T is exactly Tangent (unit vec). N is set to be Normal and B their cross   *
  406. * product.                                     *
  407. *   All agrument vectors are assumed to be normalized to a unit length.         *
  408. *                                                                            *
  409. * PARAMETERS:                                                                *
  410. *   Mat:        To place the newly computed transformation.                  *
  411. *   Trans:      Translation factor.                                          *
  412. *   Tangent:    Tangent direction to prescribe orientaion.                   *
  413. *   Normal:     Normal direction to prescribe orientaion.                    *
  414. *   Scale:      Scale factor.                                                *
  415. *   Weight:     Weight factor.                                               *
  416. *                                                                            *
  417. * RETURN VALUE:                                                              *
  418. *   void                                                                     *
  419. *****************************************************************************/
  420. static void GenTransformMatrix(CagdMType Mat,
  421.                    CagdRType *Trans,
  422.                    CagdVecStruct *Normal,
  423.                    CagdVecStruct *Tangent,
  424.                    CagdRType Scale,
  425.                    CagdRType Weight)
  426. {
  427.     int i;
  428.     CagdVecStruct B;
  429.     CagdRType
  430.     ScaleWeighted = Scale / Weight;
  431.  
  432.     CROSS_PROD(B.Vec, Tangent -> Vec, Normal -> Vec);
  433.  
  434.     for (i = 0; i < 3; i++) {
  435.     Mat[0][i] = Normal -> Vec[i] * ScaleWeighted;
  436.     Mat[1][i] = B.Vec[i] * Scale;
  437.     Mat[2][i] = Tangent -> Vec[i] * ScaleWeighted;
  438.     Mat[3][i] = Trans[i];
  439.     Mat[i][3] = 0.0;
  440.     }
  441.     Mat[3][3] = 1.0;
  442. }
  443.  
  444.  
  445. /*****************************************************************************
  446. * DESCRIPTION:                                                               M
  447. *   Routine to refine the axis curve, according to the scaling curve to      M
  448. * better approximate the requested sweep operation.                 M
  449. *                                                                            *
  450. * PARAMETERS:                                                                M
  451. *   Axis:         Axis to be used in future sweep operation with the         M
  452. *                 associated ScalingCrv.                     M
  453. *   ScalingCrv:   To use as an estimate on refinement to apply to Axis.      M
  454. *   RefLevel:     Some refinement control. Keep it low like 2 or 3.          M
  455. *                                                                            *
  456. * RETURN VALUE:                                                              M
  457. *   CagdCrvStruct *:  Refined Axis curve.                                    M
  458. *                                                                            *
  459. * KEYWORDS:                                                                  M
  460. *   CagdSweepAxisRefine, sweep, refinement                                   M
  461. *****************************************************************************/
  462. CagdCrvStruct *CagdSweepAxisRefine(CagdCrvStruct *Axis,
  463.                    CagdCrvStruct *ScalingCrv,
  464.                    int RefLevel)
  465. {
  466.     CagdBType NewScalingCrv;
  467.     CagdCrvStruct
  468.     *NewAxis = CagdCrvCopy(Axis);
  469.  
  470.     if (ScalingCrv == NULL || RefLevel < 1 || RefLevel > MAX_AXIS_REFINE_LEVEL)
  471.     return CagdCrvCopy(Axis);
  472.  
  473.     if (CAGD_IS_BEZIER_CRV(Axis)) {
  474.     CagdCrvFree(NewAxis);
  475.     NewAxis = CnvrtBezier2BsplineCrv(Axis);
  476.     }
  477.     if (CAGD_IS_PERIODIC_CRV(NewAxis)) {
  478.     CagdCrvStruct
  479.         *TCrv = CnvrtPeriodic2FloatCrv(NewAxis);
  480.  
  481.     CagdCrvFree(NewAxis);
  482.     NewAxis = TCrv;
  483.     }
  484.     if (CAGD_IS_BSPLINE_CRV(NewAxis) && !BspCrvHasOpenEC(NewAxis)) {
  485.     CagdCrvStruct
  486.         *TCrv = BspCrvOpenEnd(NewAxis);
  487.  
  488.     CagdCrvFree(NewAxis);
  489.     NewAxis = TCrv;
  490.     }
  491.  
  492.     if (CAGD_IS_BEZIER_CRV(ScalingCrv)) {
  493.     NewScalingCrv = TRUE;
  494.     ScalingCrv = CnvrtBezier2BsplineCrv(ScalingCrv);
  495.     }
  496.     else
  497.     NewScalingCrv = FALSE;
  498.  
  499.     if (CAGD_IS_BSPLINE_CRV(ScalingCrv)) {
  500.     int i, j,
  501.         SOrder = ScalingCrv -> Order,
  502.         SLength = ScalingCrv -> Length,
  503.         AOrder = NewAxis -> Order,
  504.         ALength = NewAxis -> Length;
  505.     CagdRType
  506.         *AKV = NewAxis -> KnotVector,
  507.         *KV = BspKnotCopy(ScalingCrv -> KnotVector, SLength + SOrder),
  508.         *KVRef = (RealType *) IritMalloc(sizeof(CagdRType) * RefLevel *
  509.                               (1 + SLength - SOrder));
  510.  
  511.     BspKnotAffineTrans(KV, SLength + SOrder,
  512.                AKV[AOrder - 1] - KV[SOrder - 1],
  513.                (AKV[ALength] - AKV[AOrder - 1]) /
  514.                    (KV[SLength] - KV[SOrder - 1]));
  515.  
  516.     for (i = SOrder - 1, j = 0; i < SLength; i++) {
  517.         int k;
  518.         RealType
  519.             T1 = KV[i],
  520.             T2 = KV[i+1];
  521.  
  522.         for (k = 0; k < RefLevel; k++)
  523.         KVRef[j++] = (T1 * (RefLevel - k) + T2 * k) / RefLevel;
  524.     }
  525.  
  526.     if (j > 1) {
  527.         /* Skip the first knot which is on the domain's boundary. */
  528.         Axis = CagdCrvRefineAtParams(NewAxis, FALSE, &KVRef[1], j - 1);
  529.     }
  530.     else
  531.         Axis = CagdCrvCopy(Axis);
  532.  
  533.     IritFree((VoidPtr) KV);
  534.     IritFree((VoidPtr) KVRef);
  535.     }
  536.     else
  537.         Axis = CagdCrvCopy(Axis);
  538.  
  539.     CagdCrvFree(NewAxis);
  540.     if (NewScalingCrv)
  541.     CagdCrvFree(ScalingCrv);
  542.  
  543.     return Axis;
  544. }
  545.